package cz.stv.pecinovsky.město;

import cz.stv.pecinovsky.správceplátna.Čára;
import cz.stv.pecinovsky.správceplátna.Elipsa;
import cz.stv.pecinovsky.správceplátna.IPosuvný;
import cz.stv.pecinovsky.správceplátna.Mnohotvar;
import cz.stv.pecinovsky.správceplátna.Multipřesouvač;
import cz.stv.pecinovsky.správceplátna.Obdélník;
import cz.stv.pecinovsky.správceplátna.Přesouvač;
import cz.stv.pecinovsky.správceplátna.SprávcePlátna;
import cz.stv.pecinovsky.správceplátna.Text;
import cz.stv.pecinovsky.správceplátna.Trojúhelník;

import cz.stv.pecinovsky.utility.Barva;
import cz.stv.pecinovsky.utility.IO;
import cz.stv.pecinovsky.utility.Pozice;


/*******************************************************************************
 * Testovací třída {@code OkruhTest}  slouží ke komplexnímu otestování
 * třídy {@link OkruhTest}.
 *
 * @author  Rudolf PECINOVSKÝ
 * @version 1.10.2678 — 2011-10-27
 */
public class OkruhTest extends junit.framework.TestCase
{
    private SprávcePlátna SP;
    private Okruh čtverec;
    private Okruh elko;



//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vytvoří test se zadaným názvem.
     *
     * @param název  Název konstruovaného testu
     */
    public OkruhTest( String název )
    {
        super(název);
    }



//== PŘÍPRAVA A ÚKLID PŘÍPRAVKU ================================================

    /***************************************************************************
     * Inicializace předcházející spuštění každého testu a připravující tzv.
     * přípravek (fixture), což je sada objektů, s nimiž budou testy pracovat.
     */
    @Override
    protected void setUp()
    {
        SP = SprávcePlátna.getInstance();
        čtverec = Okruh.čtverec(new Pozice(0,0));
        elko    = Okruh.elko   (new Pozice(100, 100), Barva.HNĚDÁ);
        SP.přidej(čtverec, elko);
        IO.zpráva("Okruhy připraveny");
    }


    /***************************************************************************
     * Úklid po testu - tato metoda se spustí po vykonání každého testu.
     */
    @Override
    protected void tearDown()
    {
    }


//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Zabalí zadaný posuvný objekt od okružního, výsledný okružní objekt
     * nasadí na zadané pole a vypustí jej; vrátí odkaz na přespříští pole.
     *
     * @param pole Pole, na něž bude nasazován vytvořený okružní objekt
     * @param ip   Posuvný objekt, který budeme balit do okružního
     * @return Následník následníka zadaného pole
     */
    private PoleCesty pomNasaď(PoleCesty pole, IPosuvný ip)
    {
       Okružní okružní = new Okružní(ip);
       okružní.pokračujZ(pole);
       return pole.getDalší().getDalší();
    }


    /***************************************************************************
     * Projede se se světlem po zadaném okruhu - verze používající
     * opakované volání funkce připravující budoucí hodnotu svého parametru.
     *
     * @param okruh Okruh, který se bude objíždět
     */
    private void pomObjezd(Okruh okruh)
    {
        PoleCesty počátek= okruh.getPočátek();
        Pozice    pozice = počátek.getPozice();
        Světlo    světlo = new Světlo(pozice.x, pozice.y);
        Přesouvač přes   = new Přesouvač(10);

        SP.přidej(světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        //...     Předchozí příkaz je možno opakovat kolikrát bude potřeba
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);
        počátek = pomPřesun(počátek, přes, světlo);

//        pomPřesun(počátek, přes, světlo, počátek);
    }


    /***************************************************************************
     * Projede se se světlem po zadaném okruhu - verze používající rekurzi.
     *
     * @param okruh Okruh, který se bude objíždět
     */
    private void pomObjezdRek(Okruh okruh)
    {
        PoleCesty počátek= okruh.getPočátek();
        Pozice    pozice = počátek.getPozice();
        Světlo    světlo = new Světlo(pozice.x, pozice.y);
        Přesouvač přes   = new Přesouvač(10);

        SP.přidej(světlo);

//Následjící příkaz vede na neukončené rekurzivní volání
//        pomPřesunRek(počátek, přes, světlo);

//Verze, při níž se objekt zastaví po dojetí na výchozí pole
        pomPřesunRek(počátek, přes, světlo, počátek);
    }


    /***************************************************************************
     * Přesune pomocí zadaného přesouvače zadaný posuvný objekt
     * ze zadaného pole na jeho následníka a vrátí odkaz na tohoto následníka.
     * 
     * @param pole      Pole, z nějž se vyjíždí
     * @param přesouvač Přesouvač, který objekt přesune
     * @param ip        Přesouvaný objekt
     * @return Následník výchozího pole = pole, na němž objekt skončí
     */
    private PoleCesty pomPřesun(PoleCesty pole, Přesouvač přesouvač,
                                IPosuvný ip)
    {
        PoleCesty další  = pole.getDalší();
        Pozice    pozice = další.getPozice();
        přesouvač.přesunNa(pozice, ip);
        return další;
    }


    /***************************************************************************
     * Přesouvá pomocí zadaného přesouvače zadaný posuvný objekt
     * ze zadaného výchozího pole po posloupnosti zřetězencýh polí.
     * Po každém přesunu zavolá metoda sama sebe, čímž realizuje nekonečný
     * cyklus, který směřuje k přeplnění zásobníku návratových adres.
     */
    private void pomPřesunRek(PoleCesty pole, Přesouvač přesouvač,
                              IPosuvný ip)
    {
        PoleCesty další  = pole.getDalší();
        Pozice    pozice = další.getPozice();
        přesouvač.přesunNa(pozice, ip);
        pomPřesunRek(další, přesouvač, ip);
    }


    /***************************************************************************
     * Přesouvá pomocí zadaného přesouvače zadaný posuvný objekt
     * ze zadaného výchozího pole po posloupnosti zřetězencýh polí.
     * Když dojede na zadaný počátečníá objekt, ohlásí objetí celého okruhu
     * a celou aplikaci ukončí.
     */
    private void pomPřesunRek(PoleCesty blok, Přesouvač přesouvač,
                              IPosuvný ip, PoleCesty počátek)
    {
        PoleCesty další  = blok.getDalší();
        Pozice    pozice = další.getPozice();
        přesouvač.přesunNa(pozice, ip);
        IO.konecKdyž(další.equals(počátek), "Okruh objet kolem dokola");
        pomPřesunRek(další, přesouvač, ip, počátek);
    }



//== INTERNÍ DATOVÉ TYPY =======================================================
//== VLASTNÍ TESTY =============================================================
//
//     /***************************************************************************
//      *
//      */
//     public void testXXX()
//     {
//     }

    /***************************************************************************
     *
     */
    public void testElko()
    {
       pomObjezd(elko);
    }


    /***************************************************************************
     *
     */
    public void testČtverec()
    {
       pomObjezd(čtverec);
    }


    /***************************************************************************
     *
     */
    public void testElkoDek()
    {
       Okružní okružní = new Okružní(new Světlo());
       okružní.objížděj(elko);
    }


    /***************************************************************************
     *
     */
    public void testElkoRek()
    {
       pomObjezdRek(elko);
    }


    /***************************************************************************
     *
     */
    public void testČtverecRek()
    {
       pomObjezdRek(čtverec);
    }


    /***************************************************************************
     * Na oba okruhy postupně nasadí instance všech doposud definovaných
     * typů zabalené do okružních objektů a vypustí je na cestu po okruzích.
     * Umístí je tak, aby mezi nimi bylo vždy jedno pole volné.
     * Vedle stejně vzdálených instancí pohybujících se stejnou rychlostí přidá
     * ještě jednu s poloviční rychlostí a druhou s dvojnásobnou rychlostí.
     */
    public void testSkupinový()
    {
        PoleCesty pole = čtverec.getPočátek();
        int modul = pole.getModul();

        pole = pomNasaď(pole, new Světlo());
        pole = pomNasaď(pole, new Trojúhelník(0,0,50,50));

        pole  = elko.getPočátek();
        modul = pole.getModul();

        pole = pomNasaď(pole, new Auto(0, 0, 50));
        pole = pomNasaď(pole, new Elipsa(0, 0, 50, 50));
        pole = pomNasaď(pole, new Semafor(0, 0, 50/3));
        pole = pomNasaď(pole, new Obdélník(0, 0, 50, 50, Barva.ZLATÁ));
        pole = pomNasaď(pole, new Šipka(0, 0, 50));
        pole = pomNasaď(pole, new Čára(0, 0, modul, modul, Barva.BÍLÁ));

        //Ještě jeden pomalý mnohotvar
        Mnohotvar m = new Mnohotvar("Trojobjekt", new Obdélník(),
                                    new Elipsa(), new Trojúhelník());
        m.setRozměr(50);
        Okružní o = new Okružní(m);
        o.setRychlost(50);
        o.objížděj(elko);

        //A jeden rychlý text
        o = new Okružní(new Text(0, 0, Barva.ŽLUTÁ, "RYCHLIK"));
        o.setRychlost(200);
        o.objížděj(elko);

        IO.zpráva("Až se pokocháš, stiskni OK");
        Multipřesouvač.getInstance().zastavVše();
    }


    /***************************************************************************
     * Test nasadí na malý okruh šipku a na větší šipku a auto.
     * Předvede, jak za pomoci dekorátoru {@link Otočný} objíždějí okruhy
     * přičemž jejich vzhle respektuje směr pohybu.
     */
    public void testPojízdný()
    {
        new Otočný(new Šipka()).objížděj(čtverec);
        new Otočný(new Semafor()).pokračujZ(
                čtverec.getPočátek().getDalší().getDalší());

        new Otočný(new Šipka()).objížděj(elko);
        new Otočný(new Auto()).pokračujZ(
                elko.getPočátek().getDalší().getDalší());
        new Otočný(new Semafor()).pokračujZ(
                elko.getPočátek().getDalší().getDalší().getDalší().getDalší());
    }


    /***************************************************************************
     * Otestuje, jestli se okruhy umějí přizpůsobit nové velikosti modulu
     * a odpovídajícím způsobem upravit velikost a pozici jednotlivých políček.
     */
    public void testZměnaModulu()
    {
        Obdélník referenční = new Obdélník( 50, 50, 100, 100, 
                                            Barva.MODRÁ.průsvitná());
        SP.setRozměr(8, 7);
        SP.přidej(referenční);
        IO.zpráva("Zobrazen referenční obdélník");
        for (int krok = 25;   krok < 150;   krok += 75) {
            SP.setKrok(krok);
            čtverec.setModul(krok);
            elko   .setModul(krok);
            IO.zpráva("Krok upraven na " + krok);
        }
    }

}
